/* Refer to:
 * http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html
 */
package edu.cmu.ece.ds.droidchat.controller;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.widget.ImageView;
import edu.cmu.ece.ds.droidchat.model.UserInfo;
import edu.cmu.ece.ds.droidchat.view.LoggedInUserView;

public class ImageDownloader {

	private static final String TAG = "ImageDownloader";

	public void downloadToLoggedInUserInfoView(UserInfo myInfo,
			LoggedInUserView view) {
		download(myInfo, view.getUserIconView());
	}

	public void downloadToUserInfo(String url, UserInfo uInfo,
			ImageView imageView) {
		if (cancelPotentialDownload(url, imageView)) {
			DrawableDownloaderTask task = new DrawableDownloaderTask(uInfo,
					imageView);
			DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
			imageView.setImageDrawable(downloadedDrawable);
			task.execute(url);
		}
	}

	public void download(String url, ImageView imageView) {
		downloadToUserInfo(url, null, imageView);
	}

	public void download(UserInfo uInfo, ImageView imageView) {
		downloadToUserInfo(uInfo.getProfilePictureURLString(), uInfo, imageView);
	}

	private static boolean cancelPotentialDownload(String url,
			ImageView imageView) {
		DrawableDownloaderTask bitmapDownloaderTask = getDrawableDownloaderTask(imageView);

		if (bitmapDownloaderTask != null) {
			String bitmapUrl = bitmapDownloaderTask.url;
			if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
				bitmapDownloaderTask.cancel(true);
			} else {
				// The same URL is already being downloaded.
				return false;
			}
		}
		return true;
	}

	private static DrawableDownloaderTask getDrawableDownloaderTask(
			ImageView imageView) {
		if (imageView != null) {
			Drawable drawable = imageView.getDrawable();
			if (drawable instanceof DownloadedDrawable) {
				DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
				return downloadedDrawable.getBitmapDownloaderTask();
			}
		}
		return null;
	}

	static Drawable downloadDrawable(String url) {
		try {
			if (url.contains("312100071")) {
				Log.e(TAG, "HEY!");
			}
			// Well, seems there's a bug in Drawable.createFromStream().
			// refer to
			// http://groups.google.com/group/android-developers/browse_thread/thread/4ed17d7e48899b26/
			InputStream is = new URL(url).openStream();
			Drawable d = Drawable.createFromStream(new FlushedInputStream(is),
					"src name");
			is.close();
			return d;
		} catch (IOException e) {
			Log.e(TAG, "LoadFacebookProfileImage error.");
			return null;
		}
	}

	private static InputStream fetch(String urlString)
			throws MalformedURLException, IOException {
		DefaultHttpClient httpClient = new DefaultHttpClient();
		HttpGet request = new HttpGet(urlString);
		HttpResponse response = httpClient.execute(request);
		return response.getEntity().getContent();
	}

	class DrawableDownloaderTask extends AsyncTaskExt<String, Void, Drawable> {
		private String url;
		private final WeakReference<ImageView> imageViewReference;
		private final WeakReference<UserInfo> userInfoReference;

		public DrawableDownloaderTask(UserInfo uInfo, ImageView imageView) {
			imageViewReference = new WeakReference<ImageView>(imageView);
			// uInfo can be null.
			userInfoReference = new WeakReference<UserInfo>(uInfo);
		}

		@Override
		// Actual download method, run in the task thread
		protected Drawable doInBackground(String... params) {
			// params comes from the execute() call: params[0] is the url.
			return downloadDrawable(params[0]);
		}

		@Override
		// Once the image is downloaded, associates it to the imageView
		protected void onPostExecute(Drawable d) {
			if (isCancelled()) {
				d = null;
			}

			if (imageViewReference != null) {
				ImageView imageView = imageViewReference.get();
				UserInfo uInfo = userInfoReference.get();
				DrawableDownloaderTask bitmapDownloaderTask = getDrawableDownloaderTask(imageView);
				// Change bitmap only if this process is still associated with
				// it
				if (this == bitmapDownloaderTask) {
					imageView.setImageDrawable(d);
					if (uInfo != null) {
						uInfo.setProfilePicture(d);
					}
				}
			}
		}
	}

	static class DownloadedDrawable extends ColorDrawable {
		private final WeakReference<DrawableDownloaderTask> drawableDownloaderTaskReference;

		public DownloadedDrawable(DrawableDownloaderTask drawableDownloaderTask) {
			super(Color.BLACK);
			drawableDownloaderTaskReference = new WeakReference<DrawableDownloaderTask>(
					drawableDownloaderTask);
		}

		public DrawableDownloaderTask getBitmapDownloaderTask() {
			return drawableDownloaderTaskReference.get();
		}
	}

	static class FlushedInputStream extends FilterInputStream {
		public FlushedInputStream(InputStream inputStream) {
			super(inputStream);
		}

		@Override
		public long skip(long n) throws IOException {
			long totalBytesSkipped = 0L;
			while (totalBytesSkipped < n) {
				long bytesSkipped = in.skip(n - totalBytesSkipped);
				if (bytesSkipped == 0L) {
					int b = read();
					if (b < 0) {
						break; // we reached EOF
					} else {
						bytesSkipped = 1; // we read one byte
					}
				}
				totalBytesSkipped += bytesSkipped;
			}
			return totalBytesSkipped;
		}
	}
}
